<template>
  <div class="report-history-page" v-loading="loading">
    <ReportsSearchCard title="搜索与筛选">
      <el-form :model="searchForm" class="search-form">
        <div class="search-form-content">
          <div class="search-form-fields">
            <el-form-item label="报表模板">
              <el-select
                v-model="searchForm.template_code"
                placeholder="选择模板"
                clearable
                style="width: 200px"
                popper-class="batch-dialog-select-popper"
                popper-append-to-body
              >
                <el-option label="全部" value="" />
                <el-option
                  v-for="template in templates"
                  :key="template.template_code"
                  :label="template.template_name"
                  :value="template.template_code"
                />
              </el-select>
            </el-form-item>

            <el-form-item label="状态">
              <el-select
                v-model="searchForm.status"
                placeholder="选择状态"
                clearable
                style="width: 160px"
                popper-class="batch-dialog-select-popper"
                popper-append-to-body
              >
                <el-option label="全部" value="" />
                <el-option label="成功" value="success" />
                <el-option label="失败" value="failed" />
                <el-option label="运行中" value="running" />
                <el-option label="排队中" value="queued" />
              </el-select>
            </el-form-item>

            <el-form-item label="触发方式">
              <el-select
                v-model="searchForm.trigger_type"
                placeholder="选择触发方式"
                clearable
                style="width: 160px"
                popper-class="batch-dialog-select-popper"
                popper-append-to-body
              >
                <el-option label="全部" value="" />
                <el-option label="手动" value="manual" />
                <el-option label="定时" value="scheduled" />
                <el-option label="API" value="api" />
              </el-select>
            </el-form-item>
          </div>

          <div class="search-form-actions">
            <el-button type="primary" @click="handleSearch" class="search-btn">
              <el-icon><Search /></el-icon>
              搜索
            </el-button>
            <el-button type="warning" @click="handleReset" class="reset-btn">
              <el-icon><Refresh /></el-icon>
              重置
            </el-button>
          </div>
        </div>
      </el-form>
    </ReportsSearchCard>

    <ReportsTableCard>
      <template #title>
        <el-icon><List /></el-icon>
        <span>报表历史</span>
      </template>
      <template #actions>
        <el-button type="success" @click="handleRefresh" class="refresh-btn" :loading="loading" size="small">
          <el-icon><Refresh /></el-icon>
          刷新
        </el-button>
        <div class="table-info">
          <div class="pagination-control-wrapper clickable" @click="togglePagination">
            <span class="page-count">本页 {{ runs.length }} 条</span>
            <div class="divider"></div>
            <span class="pagination-info">
              {{ currentPage }}/{{ Math.ceil(total / pageSize) || 1 }}
            </span>
            <el-icon class="pagination-toggle-icon">
              <ArrowUp v-if="paginationVisible" />
              <ArrowDown v-else />
            </el-icon>
          </div>
        </div>
      </template>

      <el-table
        :data="runs"
        @sort-change="handleSortChange"
        :default-sort="{ prop: 'created_at', order: 'descending' }"
        class="enhanced-table"
        style="width: 100%"
        :height="600"
        table-layout="fixed"
        :stripe="false"
        :border="false"
      >
        <el-table-column label="模板名称" min-width="180" show-overflow-tooltip fixed="left">
          <template #default="{ row }">
            <div class="template-name">{{ getTemplateName(row.template_code) }}</div>
          </template>
        </el-table-column>

        <el-table-column label="报表标题" min-width="200" show-overflow-tooltip>
          <template #default="{ row }">
            <div v-if="row.title" class="run-title">{{ row.title }}</div>
            <div v-else class="run-title-fallback">{{ getTemplateName(row.template_code) }}</div>
          </template>
        </el-table-column>

        <el-table-column label="统计时间" min-width="320" show-overflow-tooltip>
          <template #default="{ row }">
            <div v-if="row.time_start && row.time_end">
              {{ formatDateTime(row.time_start) }} ~ {{ formatDateTime(row.time_end) }}
            </div>
            <div v-else class="time-all">全部数据</div>
          </template>
        </el-table-column>

        <el-table-column label="触发方式" min-width="130" align="center">
          <template #default="{ row }">
            <el-tooltip 
              :content="row.trigger_type === 'scheduled'
                ? `定时任务${row.report_config_id ? ` (配置ID: ${row.report_config_id})` : ''}`
                : row.trigger_type === 'manual'
                  ? `手动运行${row.report_config_id ? ` (配置ID: ${row.report_config_id})` : ' (一次性运行)'}`
                  : row.trigger_type === 'api'
                    ? `API调用${row.report_config_id ? ` (配置ID: ${row.report_config_id})` : ' (一次性运行)'}`
                    : '未知来源'"
              placement="top"
            >
              <el-tag :type="getTriggerType(row.trigger_type)" size="small">
                <el-icon v-if="row.trigger_type === 'scheduled'"><Setting /></el-icon>
                <el-icon v-else-if="row.trigger_type === 'manual'"><VideoPlay /></el-icon>
                <el-icon v-else-if="row.trigger_type === 'api'"><Connection /></el-icon>
                {{ getTriggerText(row.trigger_type) }}
              </el-tag>
            </el-tooltip>
          </template>
        </el-table-column>

        <el-table-column label="状态" min-width="100" align="center">
          <template #default="{ row }">
            <el-tag :type="getStatusType(row.status)" size="small">
              {{ getStatusText(row.status) }}
            </el-tag>
          </template>
        </el-table-column>

        <el-table-column label="进度" min-width="120" align="center">
          <template #default="{ row }">
            <div v-if="row.status === 'running' || row.status === 'queued'" style="padding: 0 8px;">
              <el-progress 
                :percentage="row.progress || 0" 
                :color="row.progress >= 100 ? '#67c23a' : '#409eff'"
                :stroke-width="8"
              />
            </div>
            <div v-else-if="row.status === 'success'" style="color: #67c23a;">
              <el-icon><CircleCheck /></el-icon> 100%
            </div>
            <div v-else-if="row.status === 'failed'" style="color: #f56c6c;">
              <el-icon><CircleClose /></el-icon> 失败
            </div>
          </template>
        </el-table-column>

        <el-table-column label="耗时" min-width="120" align="center">
          <template #default="{ row }">
            <span v-if="row.finished_at && row.created_at" class="duration-text">
              {{ calculateDuration(row.created_at, row.finished_at) }}
            </span>
            <span v-else-if="row.status === 'running'" class="duration-running">运行中...</span>
            <span v-else class="duration-empty">-</span>
          </template>
        </el-table-column>

        <el-table-column label="创建时间" min-width="180" show-overflow-tooltip sortable="custom" prop="created_at">
          <template #default="{ row }">
            {{ formatDateTime(row.created_at) }}
          </template>
        </el-table-column>

        <el-table-column label="完成时间" min-width="180" show-overflow-tooltip sortable="custom" prop="finished_at">
          <template #default="{ row }">
            {{ formatDateTime(row.finished_at) }}
          </template>

        </el-table-column>

        <el-table-column label="操作" width="220" fixed="right">
          <template #default="{ row }">
            <div class="action-buttons">
              <!-- 前3个按钮直接显示 -->
              <el-tooltip content="查看详情" placement="top">
                <el-button
                  type="success"
                  size="small"
                  @click="handleViewDetail(row)"
                  circle
                >
                  <el-icon><View /></el-icon>
                </el-button>
              </el-tooltip>
              <el-tooltip content="再次生成" placement="top">
                <el-button
                  type="warning"
                  size="small"
                  @click="handleRegenerate(row)"
                  circle
                >
                  <el-icon><Refresh /></el-icon>
                </el-button>
              </el-tooltip>
              <el-tooltip :content="row.status === 'success' ? '下载报表' : '报表未生成'" placement="top">
                <el-button
                  type="primary"
                  size="small"
                  @click="handleDownload(row)"
                  :disabled="row.status !== 'success'"
                  circle
                >
                  <el-icon><Download /></el-icon>
                </el-button>
              </el-tooltip>
              
              <!-- 更多操作下拉菜单 -->
              <el-tooltip content="更多操作" placement="top">
                <el-dropdown @command="(command) => handleMoreAction(command, row)" trigger="click">
                  <el-button type="info" size="small" circle>
                    <el-icon><MoreFilled /></el-icon>
                  </el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item 
                        command="notify"
                        :disabled="row.status !== 'success'"
                      >
                        <el-icon><Message /></el-icon>
                        发送通知
                      </el-dropdown-item>
                      <el-dropdown-item command="delete" divided>
                        <el-icon><Delete /></el-icon>
                        <span style="color: #f56c6c;">删除记录</span>
                      </el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </el-tooltip>
            </div>
          </template>
        </el-table-column>
      </el-table>

      <template #pagination>
        <transition name="pagination-slide">
          <div v-show="paginationVisible" class="pagination-section">
            <el-pagination
              v-model:current-page="currentPage"
              v-model:page-size="pageSize"
              :total="total"
              :page-sizes="[10, 20, 50, 100]"
              layout="total, sizes, prev, pager, next, jumper"
              @size-change="handleSizeChange"
              @current-change="handlePageChange"
              background
            />
          </div>
        </transition>
      </template>
    </ReportsTableCard>

    <!-- 查看详情 -->
    <el-dialog
      v-model="detailDialogVisible"
      width="900px"
      :close-on-click-modal="false"
      append-to-body
      class="unified-dialog"
    >
      <template #header>
        <DialogHeader
          icon="View"
          title="报表详情"
          :description="detailDialogData?.title || getTemplateName(detailDialogData?.template_code || '')"
          type="success"
        />
      </template>

      <div v-loading="detailDialogLoading">
        <el-descriptions :column="2" border v-if="detailDialogData">
          <el-descriptions-item label="模板名称">
            {{ getTemplateName(detailDialogData.template_code) || '-' }}
          </el-descriptions-item>
          <el-descriptions-item label="运行状态">
            <el-tag :type="getStatusType(detailDialogData.status)">
              {{ getStatusText(detailDialogData.status) }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="运行进度">
            <div v-if="detailDialogData.status === 'running' || detailDialogData.status === 'queued'" style="padding: 0 8px;">
              <el-progress 
                :percentage="detailDialogData.progress || 0" 
                :color="detailDialogData.progress >= 100 ? '#67c23a' : '#409eff'"
              />
            </div>
            <div v-else-if="detailDialogData.status === 'success'" style="color: #67c23a;">
              <el-icon><CircleCheck /></el-icon> 100%
            </div>
            <div v-else-if="detailDialogData.status === 'failed'" style="color: #f56c6c;">
              <el-icon><CircleClose /></el-icon> 失败
            </div>
          </el-descriptions-item>
          <el-descriptions-item label="触发方式">
            <el-tag :type="getTriggerType(detailDialogData.trigger_type)" size="small">
              <el-icon v-if="detailDialogData.trigger_type === 'scheduled'"><Setting /></el-icon>
              <el-icon v-else-if="detailDialogData.trigger_type === 'manual'"><VideoPlay /></el-icon>
              <el-icon v-else-if="detailDialogData.trigger_type === 'api'"><Connection /></el-icon>
              {{ getTriggerText(detailDialogData.trigger_type) }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="耗时">
            {{ calculateDuration(detailDialogData.created_at, detailDialogData.finished_at) }}
          </el-descriptions-item>
          <el-descriptions-item label="统计时间" :span="2">
            <span v-if="detailDialogData.time_start && detailDialogData.time_end">
              {{ formatDateTime(detailDialogData.time_start) }} ~ {{ formatDateTime(detailDialogData.time_end) }}
            </span>
            <span v-else>全部数据</span>
          </el-descriptions-item>
          <el-descriptions-item label="创建时间">
            {{ formatDateTime(detailDialogData.created_at) }}
          </el-descriptions-item>
          <el-descriptions-item label="完成时间">
            {{ formatDateTime(detailDialogData.finished_at) }}
          </el-descriptions-item>
        </el-descriptions>

        <div class="detail-section">
          <div class="section-header">
            <div class="section-title">运行参数</div>
            <div class="section-actions">
              <el-button
                text
                size="small"
                type="primary"
                @click="toggleParamJson"
              >
                <el-icon :class="['json-toggle-icon', { expanded: detailParamsExpanded }]">
                  <ArrowDown />
                </el-icon>
                {{ detailParamsExpanded ? '收起' : '展开' }}
              </el-button>
              <el-divider direction="vertical" class="json-action-divider" />
              <el-button
                text
                size="small"
                type="primary"
                @click="handleCopyJson(detailDialogData?.params, '运行参数')"
              >
                <el-icon><CopyDocument /></el-icon>
                复制
              </el-button>
            </div>
          </div>
          <transition name="json-collapse">
            <pre v-show="detailParamsExpanded" class="json-block">{{ formatJson(detailDialogData?.params) }}</pre>
          </transition>
          <div v-show="!detailParamsExpanded" class="json-collapsed-placeholder">
            JSON 内容较长，点击“展开”查看
          </div>
        </div>

        <div
          class="detail-section"
          v-if="detailDialogData?.files && detailDialogData.files.length"
        >
          <div class="section-header">
            <div class="section-title">生成文件</div>
            <el-button
              text
              type="primary"
              size="small"
              @click="downloadAllFiles(detailDialogData)"
            >
              批量下载
            </el-button>
          </div>
          <el-table :data="detailDialogData.files" border stripe class="detail-files-table">
            <el-table-column prop="file_type" label="类型" width="120">
              <template #default="{ row }">
                <el-tag type="primary">{{ row.file_type?.toUpperCase() || '-' }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="file_path" label="文件路径" min-width="260" show-overflow-tooltip />
            <el-table-column prop="file_size" label="大小" width="120">
              <template #default="{ row }">
                {{ formatFileSize(row.file_size) }}
              </template>
            </el-table-column>
            <el-table-column label="操作" width="160" align="center">
              <template #default="{ row }">
                <el-button
                  type="primary"
                  text
                  size="small"
                  @click="downloadFile(detailDialogData.id, row.id, row.file_type)"
                >下载</el-button>
                <el-button
                  v-if="row.file_type !== 'excel'"
                  type="info"
                  text
                  size="small"
                  @click="previewFile(detailDialogData.id, row.id)"
                >预览</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </div>

      <template #footer>
        <div class="dialog-footer">
          <el-button @click="detailDialogVisible = false">关闭</el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 下载对话框 -->
    <el-dialog
      v-model="downloadDialogVisible"
      width="680px"
      :close-on-click-modal="false"
      append-to-body
      class="unified-dialog"
    >
      <template #header>
        <DialogHeader
          icon="Download"
          title="下载报表"
          :description="getTemplateName(downloadDialogData?.template_code || '')"
          type="primary"
        />
      </template>

      <div v-loading="downloadDialogLoading">
        <el-alert
          v-if="downloadDialogData?.title"
          type="info"
          :closable="false"
          show-icon
          style="margin-bottom: 16px"
        >
          报表标题：{{ downloadDialogData.title }}
        </el-alert>
        <el-table
          v-if="downloadDialogData?.files?.length"
          :data="downloadDialogData.files"
          border
          class="download-files-table"
        >
          <el-table-column label="格式" width="120">
            <template #default="{ row }">
              <el-tag type="primary">{{ row.file_type?.toUpperCase() }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column label="大小" width="120">
            <template #default="{ row }">
              {{ formatFileSize(row.file_size) }}
            </template>
          </el-table-column>
          <el-table-column prop="file_path" label="文件路径" show-overflow-tooltip />
          <el-table-column label="操作" width="140" align="center">
            <template #default="{ row }">
              <el-button type="primary" text size="small" @click="downloadFile(downloadDialogData.id, row.id, row.file_type)">
                下载
              </el-button>
              <el-button 
                v-if="row.file_type !== 'excel'"
                type="info" 
                text 
                size="small" 
                @click="previewFile(downloadDialogData.id, row.id)"
              >
                预览
              </el-button>
            </template>
          </el-table-column>
        </el-table>
        <div v-else class="empty-tip">暂无可下载的文件</div>
      </div>

      <template #footer>
        <div class="dialog-footer">
          <el-button @click="downloadDialogVisible = false">关闭</el-button>
          <el-button
            type="primary"
            @click="downloadAllFiles(downloadDialogData)"
            :disabled="!downloadDialogData?.files?.length"
          >
            批量下载
          </el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 发送通知 -->
    <el-dialog
      v-model="notifyDialogVisible"
      width="640px"
      :close-on-click-modal="false"
      append-to-body
      class="unified-dialog"
    >
      <template #header>
        <DialogHeader
          icon="Message"
          title="发送报表通知"
          description="选择渠道并发送本次报表"
          type="danger"
        />
      </template>

      <el-form :model="notifyForm" label-width="120px">
        <el-form-item label="通知渠道">
          <el-checkbox-group v-model="notifyForm.channels" @change="handleChannelsChange">
            <el-checkbox label="wecom">企业微信</el-checkbox>
            <el-checkbox label="email" disabled>
              邮件
              <el-tag type="info" size="small" style="margin-left: 8px">待开发</el-tag>
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>

        <template v-if="notifyForm.channels.includes('wecom') && notifyForm.channelConfigs.wecom">
          <el-form-item label="通知地址">
            <el-select
              v-model="notifyForm.channelConfigs.wecom.webhook_id"
              placeholder="请选择企业微信 Webhook"
              filterable
              clearable
              style="width: 100%"
              popper-class="batch-dialog-select-popper"
              popper-append-to-body
              :loading="loadingWebhooks"
            >
              <el-option
                v-for="webhook in getWebhooksByChannel('wecom')"
                :key="webhook.id"
                :label="getWebhookOptionLabel(webhook)"
                :value="webhook.id"
              >
                <div class="webhook-option">
                  <div class="webhook-name">{{ webhook.name || '未命名' }}</div>
                  <div class="webhook-desc" v-if="getWebhookOptionDetail(webhook)">
                    {{ getWebhookOptionDetail(webhook) }}
                  </div>
                </div>
              </el-option>
            </el-select>
            <div class="field-tip">请先在“通知管理”配置 Webhook，再在此处选择</div>
          </el-form-item>

          <el-form-item label="@指定成员">
            <el-input
              v-model="notifyForm.channelConfigs.wecom.wecom_mentioned_list"
              placeholder="填写成员 ID，多个用英文逗号分隔"
            />
            <div class="field-tip">示例：zhangjie,baijunguang</div>
          </el-form-item>

          <el-form-item label="@所有人">
            <el-switch v-model="notifyForm.channelConfigs.wecom.wecom_mentioned_all" />
          </el-form-item>
        </template>
      </el-form>

      <template #footer>
        <div class="dialog-footer">
          <el-button @click="notifyDialogVisible = false">取消</el-button>
          <el-button type="primary" @click="handleSendNotify">发送</el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 再次生成对话框 -->
    <ReportRegenerateDialog
      v-model="regenerateDialogVisible"
      :run-id="regeneratingRunId"
      @generated="handleRegenerateSuccess"
    />
  </div>
</template>

<script setup>
import { ref, reactive, onMounted, onUnmounted, nextTick, watch, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search, Refresh, List, ArrowUp, ArrowDown, View, Download, Message, Delete, CopyDocument, MoreFilled, CircleCheck, CircleClose, Setting, VideoPlay, Connection } from '@element-plus/icons-vue'
import ReportsSearchCard from '../components/layout/ReportsSearchCard.vue'
import ReportsTableCard from '../components/layout/ReportsTableCard.vue'
import DialogHeader from '@/components/DialogHeader.vue'
import ReportRegenerateDialog from '../components/configs/ReportRegenerateDialog.vue'
import api from '@/utils/api'
import { createLogger } from '@/utils/logger'

const componentLogger = createLogger('ReportHistoryPage')

const route = useRoute()
const router = useRouter()
const loading = ref(false)
const runs = ref([])
const pollingIntervalMs = 2000
let pollingTimer = null
const templates = ref([])
const currentPage = ref(1)
const pageSize = ref(20)
const total = ref(0)
const paginationVisible = ref(false)
const detailDialogVisible = ref(false)
const detailDialogData = ref(null)
const detailDialogLoading = ref(false)
const detailParamsExpanded = ref(false)
const downloadDialogVisible = ref(false)
const downloadDialogData = ref(null)
const downloadDialogLoading = ref(false)
const notifyDialogVisible = ref(false)
const notifyDialogLoading = ref(false)
const notifyDialogData = ref(null)
const regenerateDialogVisible = ref(false)
const regeneratingRunId = ref(null)
const notifyForm = reactive({
  runId: null,
  channels: [],
  channelConfigs: {}
})
const webhooksList = ref([])
const loadingWebhooks = ref(false)

// 來自路由的額外篩選：指定任務ID、運行ID與是否包含一次性運行
const selectedReportConfigId = ref(null)
const includeAll = ref(false)
const runId = ref(null)  // 运行ID（用于精确匹配单条记录）

const searchForm = reactive({
  template_code: '',
  status: '',
  trigger_type: '',
  sortField: 'created_at',
  sortOrder: 'desc'
})

const fetchTemplates = async () => {
  try {
    const response = await api.get('/reports/templates', { params: { page: 1, size: 1000 } })
    templates.value = response.data.data.rows || []
    componentLogger.info('模板列表加载成功', { count: templates.value.length })
  } catch (error) {
    componentLogger.error('获取模板列表失败', error)
  }
}

// 获取报表历史
const hasActiveRuns = computed(() =>
  runs.value.some(run => run.status === 'running' || run.status === 'queued')
)

const stopPolling = () => {
  if (pollingTimer) {
    clearInterval(pollingTimer)
    pollingTimer = null
  }
}

const startPolling = () => {
  stopPolling()
  if (hasActiveRuns.value) {
    pollingTimer = setInterval(() => {
      fetchRuns({ silent: true })
    }, pollingIntervalMs)
  }
}

const fetchRuns = async ({ silent = false } = {}) => {
  if (!silent) {
    loading.value = true
  }
  
  try {
    const params = {
      page: currentPage.value,
      size: pageSize.value
    }
    
    // run_id 优先级最高，如果存在则只使用 run_id 筛选（不应用其他条件）
    if (runId.value) {
      params.run_id = runId.value
      // 添加排序参数（run_id 筛选时仍然可以排序）
      if (searchForm.sortField) {
        params.sort_field = searchForm.sortField
        params.sort_order = searchForm.sortOrder
      }
    } else {
      // 只有当没有 run_id 时，才应用其他筛选条件
      if (searchForm.template_code) {
        params.template_code = searchForm.template_code
      }
      
      if (searchForm.status) {
        params.status = searchForm.status
      }
      
      if (searchForm.trigger_type) {
        params.trigger_type = searchForm.trigger_type
      }
      
      if (searchForm.sortField) {
        params.sort_field = searchForm.sortField
        params.sort_order = searchForm.sortOrder
      }
      
      if (selectedReportConfigId.value) {
        params.report_config_id = selectedReportConfigId.value
        if (includeAll.value) {
          params.include_all = 1
        }
      }
    }
    
    const response = await api.get('/reports/runs', { params })
    const newRows = response.data.data.rows || []
    const newTotal = response.data.data.total || 0
    const hasChanges = newRows.length !== runs.value.length || newRows.some((row, idx) => row.id !== runs.value[idx]?.id || row.status !== runs.value[idx]?.status || row.progress !== runs.value[idx]?.progress)
    runs.value = newRows
    total.value = newTotal
    
    componentLogger.info('报表历史加载成功', { 
      count: runs.value.length, 
      total: total.value 
    })
  } catch (error) {
    componentLogger.error('获取报表历史失败', error)
    ElMessage.error('获取报表历史失败')
  } finally {
    if (!silent) {
      loading.value = false
    }
    startPolling()
  }
}

const getTemplateName = (code) => {
  const template = templates.value.find(t => t.template_code === code)
  return template ? template.template_name : code
}

const getTriggerType = (triggerType) => {
  const map = {
    manual: 'primary',
    scheduled: 'success',
    api: 'info'
  }
  return map[triggerType] || 'info'
}

const getTriggerText = (triggerType) => {
  const map = {
    manual: '手动',
    scheduled: '定时',
    api: 'API'
  }
  return map[triggerType] || triggerType
}

const getStatusType = (status) => {
  const map = {
    success: 'success',
    failed: 'danger',
    running: 'warning',
    queued: 'info'
  }
  return map[status] || 'info'
}

const getStatusText = (status) => {
  const map = {
    success: '成功',
    failed: '失败',
    running: '运行中',
    queued: '排队中'
  }
  return map[status] || status
}

const formatDateTime = (dateTimeStr) => {
  if (!dateTimeStr) return '-'

  if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(dateTimeStr)) {
    return dateTimeStr
  }

  if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(dateTimeStr) && !dateTimeStr.includes('Z') && !dateTimeStr.match(/[+-]\d{2}:\d{2}$/)) {
    const match = dateTimeStr.match(/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})/)
    if (match) {
      return `${match[1]} ${match[2]}`
    }
  }

  try {
    const date = new Date(dateTimeStr)
    if (isNaN(date.getTime())) {
      return dateTimeStr
    }

    const formatter = new Intl.DateTimeFormat('zh-CN', {
      timeZone: 'Asia/Shanghai',
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit',
      hour12: false
    })

    const parts = formatter.formatToParts(date)
    const year = parts.find(p => p.type === 'year').value
    const month = parts.find(p => p.type === 'month').value
    const day = parts.find(p => p.type === 'day').value
    const hour = parts.find(p => p.type === 'hour').value
    const minute = parts.find(p => p.type === 'minute').value
    const second = parts.find(p => p.type === 'second').value

    return `${year}-${month}-${day} ${hour}:${minute}:${second}`
  } catch (error) {
    return dateTimeStr
  }
}

const calculateDuration = (start, end) => {
  if (!start || !end) return '-'
  const startTime = new Date(start).getTime()
  const endTime = new Date(end).getTime()
  if (isNaN(startTime) || isNaN(endTime) || endTime <= startTime) return '-'
  const diff = Math.floor((endTime - startTime) / 1000)
  const minutes = Math.floor(diff / 60)
  const seconds = diff % 60
  if (minutes === 0) return `${seconds}s`
  return `${minutes}m ${seconds}s`
}

const handleSearch = () => {
  // 清除URL参数中的筛选条件（但保留 tab 参数）
  const newQuery = { ...route.query }
  delete newQuery.report_config_id
  delete newQuery.run_id
  delete newQuery.template_code
  delete newQuery.include_all
  // 保留 tab 参数
  if (route.query.tab) {
    newQuery.tab = route.query.tab
  }
  
  // 更新路由
  router.replace({ query: newQuery })
  
  // 清除内部状态
  selectedReportConfigId.value = null
  includeAll.value = false
  runId.value = null
  
  currentPage.value = 1
  fetchRuns()
}

const handleReset = () => {
  searchForm.template_code = ''
  searchForm.status = ''
  searchForm.trigger_type = ''
  searchForm.sortField = 'created_at'
  searchForm.sortOrder = 'desc'
  
  // 清除 URL 参数中的筛选条件（但保留 tab 参数）
  const newQuery = { ...route.query }
  delete newQuery.report_config_id
  delete newQuery.run_id
  delete newQuery.template_code
  delete newQuery.include_all
  // 保留 tab 参数
  if (route.query.tab) {
    newQuery.tab = route.query.tab
  }
  
  // 更新路由并重新获取数据
  router.replace({ query: newQuery })
  
  // 清除内部状态
  selectedReportConfigId.value = null
  includeAll.value = false
  runId.value = null
  
  currentPage.value = 1
  fetchRuns()
}

const handleRefresh = () => {
  // 清除 URL 参数中的筛选条件（但保留 tab 参数）
  const newQuery = { ...route.query }
  delete newQuery.report_config_id
  delete newQuery.run_id
  delete newQuery.template_code
  delete newQuery.include_all
  // 保留 tab 参数
  if (route.query.tab) {
    newQuery.tab = route.query.tab
  }
  
  // 清除内部状态
  selectedReportConfigId.value = null
  includeAll.value = false
  runId.value = null
  
  // 重置搜索表单
  searchForm.template_code = ''
  searchForm.status = ''
  searchForm.trigger_type = ''
  searchForm.sortField = 'created_at'
  searchForm.sortOrder = 'desc'
  
  // 重置页码
  currentPage.value = 1
  
  // 更新路由并重新获取数据
  router.replace({ query: newQuery })
  fetchRuns()
  fetchTemplates()
  ElMessage.success('刷新成功')
}

const handleSizeChange = () => {
  currentPage.value = 1
  fetchRuns()
}

const handlePageChange = () => {
  fetchRuns()
}

// 从URL参数中应用筛选条件
const applyFiltersFromRoute = () => {
  const q = route.query
  
  // 读取 run_id（优先级最高）
  if (q.run_id) {
    runId.value = String(q.run_id)
    selectedReportConfigId.value = null
    includeAll.value = false
    // run_id 筛选时，不设置搜索表单的 template_code（因为这些筛选条件不在搜索表单中显示）
    // 但不清除其他搜索表单字段（status、trigger_type），因为用户可能想看到完整的筛选状态
  } else {
    runId.value = null
    // 读取 report_config_id
    if (q.report_config_id) {
      selectedReportConfigId.value = Number(q.report_config_id)
      // 当有 report_config_id 时，清除搜索表单中的 template_code（因为它不在搜索表单中显示）
      // 但保留其他搜索表单字段
    } else {
      selectedReportConfigId.value = null
    }
    includeAll.value = q.include_all === '1' || q.include_all === 1 || q.include_all === true
    
    // 读取 template_code（仅用于搜索表单显示，当没有 report_config_id 或 run_id 时）
    if (q.template_code && !q.report_config_id && !q.run_id) {
      searchForm.template_code = String(q.template_code)
    } else if (q.report_config_id || q.run_id) {
      // 当有 report_config_id 或 run_id 时，清除搜索表单中的 template_code
      // 因为这些筛选条件是通过 URL 参数传递的，不在搜索表单中显示
      if (searchForm.template_code && !q.template_code) {
        searchForm.template_code = ''
      }
    }
  }
  
  // 如果 URL 中没有筛选参数，清除所有内部状态
  if (!q.run_id && !q.report_config_id && !q.template_code && !q.include_all) {
    selectedReportConfigId.value = null
    includeAll.value = false
    runId.value = null
  }
}

const handleSortChange = ({ prop, order }) => {
  if (prop) {
    searchForm.sortField = prop
    searchForm.sortOrder = order === 'ascending' ? 'asc' : 'desc'
    fetchRuns()
  }
}

const togglePagination = () => {
  paginationVisible.value = !paginationVisible.value
}

const handleViewDetail = async (row) => {
  detailDialogLoading.value = true
  try {
    const response = await api.get(`/reports/runs/${row.id}`)
    detailDialogData.value = response.data.data
    detailParamsExpanded.value = false
    detailDialogVisible.value = true
    componentLogger.info('查看报表详情', { run_id: row.id })
  } catch (error) {
    componentLogger.error('获取报表详情失败', error)
    ElMessage.error('获取报表详情失败')
  } finally {
    detailDialogLoading.value = false
  }
}

const handleRegenerate = (row) => {
  if (!row?.id) return
  regeneratingRunId.value = row.id
  regenerateDialogVisible.value = true
  componentLogger.info('打开再次生成对话框', { run_id: row.id })
}

const handleRegenerateSuccess = () => {
  // 刷新列表
  fetchRuns()
  componentLogger.info('报表重新生成成功')
}

const handleDownload = async (row) => {
  if (row.status !== 'success') {
    ElMessage.warning('报表未成功生成，无法下载')
    return
  }

  downloadDialogLoading.value = true
  try {
    const response = await api.get(`/reports/runs/${row.id}`)
    const data = response.data.data
    if (!data.files || !data.files.length) {
      ElMessage.warning('暂无可下载的文件')
      return
    }
    downloadDialogData.value = data
    downloadDialogVisible.value = true
    componentLogger.info('打开下载对话框', { run_id: row.id })
  } catch (error) {
    componentLogger.error('获取下载信息失败', error)
    ElMessage.error('获取文件列表失败')
  } finally {
    downloadDialogLoading.value = false
  }
}

const handleNotify = async (row) => {
  if (row.status !== 'success') {
    ElMessage.warning('报表未成功生成，无法发送通知')
    return
  }

  notifyForm.runId = row.id
  notifyForm.channels = []
  notifyForm.channelConfigs = {}

  await fetchWebhooks()
  await nextTick()
  notifyDialogVisible.value = true
}

const handleMoreAction = (command, row) => {
  if (command === 'notify') {
    handleNotify(row)
  } else if (command === 'delete') {
    confirmDelete(row)
  }
}

const handleDelete = async (row) => {
  try {
    loading.value = true
    await api.delete(`/reports/runs/${row.id}`)
    ElMessage.success('删除成功')
    componentLogger.info('删除报表历史成功', { run_id: row.id })
    fetchRuns()
  } catch (error) {
    const message = error.response?.data?.message || '删除失败'
    componentLogger.error('删除报表历史失败', error)
    ElMessage.error(message)
  } finally {
    loading.value = false
  }
}

const confirmDelete = async (row) => {
  if (!row?.id) {
    ElMessage.error('记录 ID 不存在，无法删除')
    return
  }

  const reportTitle = row.title || getTemplateName(row.template_code)

  try {
    await ElMessageBox.confirm(
      `确定要删除报表 <strong>${reportTitle || '未命名报表'}</strong> 吗？此操作不可恢复。`,
      '删除确认',
      {
        confirmButtonText: '删除',
        cancelButtonText: '取消',
        type: 'warning',
        dangerouslyUseHTMLString: true,
        closeOnClickModal: false
      }
    )
    await handleDelete(row)
  } catch (error) {
    if (error !== 'cancel') {
      componentLogger.warn('删除操作被取消或失败', error)
    }
  }
}

const formatFileSize = (bytes) => {
  if (!bytes) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`
}

const formatJson = (json) => {
  if (!json) return '-'
  try {
    return JSON.stringify(json, null, 2)
  } catch (error) {
    return String(json)
  }
}

const handleCopyJson = async (json, label) => {
  const text = formatJson(json)
  try {
    await navigator.clipboard.writeText(text)
    ElMessage.success(`${label} JSON 已复制`)
  } catch (error) {
    componentLogger.error('复制 JSON 失败', error)
    ElMessage.error('复制失败，请手动复制')
  }
}

const toggleParamJson = () => {
  detailParamsExpanded.value = !detailParamsExpanded.value
}

const downloadFile = (runId, fileId, fileType) => {
  if (!runId || !fileId) return
  const downloadUrl = `/api/reports/runs/${runId}/files/${fileId}/download`
  window.open(downloadUrl, '_blank')
  ElMessage.success(`正在下载 ${fileType?.toUpperCase() || ''} 文件`)
}

const previewFile = (runId, fileId) => {
  if (!runId || !fileId) return
  const previewUrl = `/api/reports/runs/${runId}/files/${fileId}/preview`
  window.open(previewUrl, '_blank')
}

const downloadAllFiles = (data) => {
  if (!data?.files || !data.files.length) {
    ElMessage.warning('暂无可下载的文件')
    return
  }
  data.files.forEach((file, index) => {
    setTimeout(() => {
      downloadFile(data.id, file.id, file.file_type)
    }, index * 300)
  })
}

const fetchWebhooks = async () => {
  loadingWebhooks.value = true
  try {
    const response = await api.get('/reports/webhooks')
    if (response.data.success) {
      webhooksList.value = response.data.data || []
      componentLogger.info('Webhook 列表加载成功', { count: webhooksList.value.length })
    }
  } catch (error) {
    componentLogger.error('获取 Webhook 列表失败', error)
    ElMessage.error('获取 Webhook 列表失败')
  } finally {
    loadingWebhooks.value = false
  }
}

const getWebhooksByChannel = (channelType) => {
  return webhooksList.value.filter(item => item.channel_type === channelType)
}

const getWebhookOptionLabel = (webhook) => {
  return webhook.name || webhook.url || '未命名'
}

const getWebhookOptionDetail = (webhook) => {
  if (webhook.remark) return webhook.remark
  if (webhook.url) return webhook.url
  return ''
}

const handleChannelsChange = (channels) => {
  const enabledChannels = channels.filter(ch => ch !== 'email')

  enabledChannels.forEach(channel => {
    if (!notifyForm.channelConfigs[channel]) {
      notifyForm.channelConfigs[channel] = {
        webhook_id: null,
        wecom_mentioned_list: '',
        wecom_mentioned_all: false
      }
    }
  })

  Object.keys(notifyForm.channelConfigs).forEach(channel => {
    if (!enabledChannels.includes(channel)) {
      delete notifyForm.channelConfigs[channel]
    }
  })

  if (channels.length > 0 && channels.every(ch => ch === 'email')) {
    notifyForm.channels = []
  }
}

const handleSendNotify = async () => {
  const enabledChannels = notifyForm.channels.filter(ch => ch !== 'email')

  if (enabledChannels.length === 0) {
    ElMessage.warning('请选择通知渠道')
    return
  }

  for (const channel of enabledChannels) {
    const config = notifyForm.channelConfigs[channel]
    if (!config || !config.webhook_id) {
      ElMessage.warning(`请选择${channel === 'wecom' ? '企业微信' : channel}渠道的通知地址`)
      return
    }
  }

  try {
    const notifyParams = { send_notification: true }
    const webhookIds = []

    enabledChannels.forEach(channel => {
      const config = notifyForm.channelConfigs[channel]
      if (config?.webhook_id) {
        webhookIds.push(config.webhook_id)
      }
    })

    let wecomMentionObj = null
    const wecomConfig = notifyForm.channelConfigs.wecom
    if (wecomConfig) {
      const mentioned = (wecomConfig.wecom_mentioned_list || '').trim()
      wecomMentionObj = {
        to_user: mentioned ? mentioned.split(',').map(item => item.trim()).filter(Boolean) : [],
        to_party: [],
        to_tag: [],
        is_all: Boolean(wecomConfig.wecom_mentioned_all)
      }
    }

    const hasWecomMention = Boolean(
      wecomMentionObj && (wecomMentionObj.is_all || (wecomMentionObj.to_user && wecomMentionObj.to_user.length > 0))
    )

    if (webhookIds.length > 0 && !hasWecomMention) {
      notifyParams.webhook_ids = webhookIds
    } else {
      const webhooks = []
      webhookIds.forEach(id => {
        const webhook = webhooksList.value.find(item => String(item.id) === String(id))
        if (webhook) {
          const resolved = {
            channel_type: webhook.channel_type,
            webhook_url: webhook.url || webhook.webhook_url || webhook.webhookUrl || ''
          }
          if (resolved.channel_type === 'wecom' && hasWecomMention) {
            resolved.wecom_mention = wecomMentionObj
          }
          webhooks.push(resolved)
        }
      })

      if (!webhooks.length) {
        ElMessage.warning('请配置至少一个通知渠道')
        return
      }
      notifyParams.webhooks = webhooks
    }

    await api.post(`/reports/runs/${notifyForm.runId}/notify`, {
      notify_params: notifyParams
    })

    ElMessage.success('通知发送成功')
    notifyDialogVisible.value = false
    componentLogger.info('通知发送成功', { run_id: notifyForm.runId })
  } catch (error) {
    componentLogger.error('通知发送失败', error)
    ElMessage.error(error.response?.data?.message || '通知发送失败')
  }
}

// 检查是否是浏览器刷新（而非路由跳转）
const isPageReload = () => {
  // 使用 performance API 检查页面加载类型
  if (typeof performance !== 'undefined' && performance.getEntriesByType) {
    const navEntries = performance.getEntriesByType('navigation')
    if (navEntries.length > 0) {
      const navEntry = navEntries[0]
      // 'reload' 表示浏览器刷新（F5）
      return navEntry.type === 'reload'
    }
  }
  // 降级方案：检查 performance.navigation（旧版 API）
  if (typeof performance !== 'undefined' && performance.navigation) {
    // 1 表示 TYPE_RELOAD（浏览器刷新）
    return performance.navigation.type === 1
  }
  // 如果无法判断，默认返回 false（保留筛选条件，避免误清除）
  return false
}

onMounted(() => {
  fetchTemplates()
  
  // 检查是否是浏览器刷新
  if (isPageReload()) {
    // 浏览器刷新时：清除筛选条件，与刷新按钮行为一致
    const newQuery = { ...route.query }
    const hasFilterParams = newQuery.report_config_id || newQuery.run_id || newQuery.template_code || newQuery.include_all
    
    if (hasFilterParams) {
      delete newQuery.report_config_id
      delete newQuery.run_id
      delete newQuery.template_code
      delete newQuery.include_all
      // 保留 tab 参数
      if (route.query.tab) {
        newQuery.tab = route.query.tab
      }
      
      // 清除内部状态
      selectedReportConfigId.value = null
      includeAll.value = false
      runId.value = null
      
      // 重置搜索表单
      searchForm.template_code = ''
      searchForm.status = ''
      searchForm.trigger_type = ''
      searchForm.sortField = 'created_at'
      searchForm.sortOrder = 'desc'
      
      // 重置页码
      currentPage.value = 1
      
      // 更新路由（不触发 watch，避免重复调用 fetchRuns）
      router.replace({ query: newQuery })
    }
  } else {
    // 路由跳转时：保留筛选条件（从其他页面跳转过来）
    applyFiltersFromRoute()
  }
  
  fetchRuns()
})

// 監聽路由變化，外部二次跳轉時自動更新
watch(() => route.query, () => {
  applyFiltersFromRoute()
  currentPage.value = 1
  fetchRuns()
}, { deep: true })

watch(hasActiveRuns, (active) => {
  if (active) {
    startPolling()
  } else {
    stopPolling()
  }
})

onUnmounted(() => {
  stopPolling()
})
</script>

<style scoped>
.report-history-page {
  padding: 0;
}

.search-form-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 20px;
}

.search-form-fields {
  display: flex;
  gap: 20px;
  flex: 1;
}

.search-form-fields .el-form-item {
  margin-bottom: 0;
}

.search-form-actions {
  display: flex;
  gap: 10px;
  white-space: nowrap;
}

.template-name {
  font-weight: 500;
}

.run-title {
  font-weight: 500;
  color: #409eff;
}

.run-title-fallback {
  color: #999;
  font-style: italic;
}

.time-all {
  color: #409eff;
  font-weight: 500;
}

.duration-text {
  color: #606266;
}

.duration-running {
  color: #e6a23c;
}

.duration-empty {
  color: #999;
}

.table-info {
  display: flex;
  align-items: center;
}

.refresh-btn {
  background: linear-gradient(135deg, #67C23A 0%, #85CE61 100%);
  border: none;
  border-radius: 8px;
  color: #fff;
  box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-weight: 500;
}

.refresh-btn:hover {
  background: linear-gradient(135deg, #85CE61 0%, #67C23A 100%);
  box-shadow: 0 6px 20px rgba(103, 194, 58, 0.4);
  transform: translateY(-2px);
  color: #fff;
}

.pagination-control-wrapper {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  background: rgba(64, 158, 255, 0.08);
  border: 1px solid rgba(64, 158, 255, 0.2);
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  user-select: none;
  font-size: 12px;
  height: 24px;
}

.pagination-control-wrapper:hover {
  background: rgba(64, 158, 255, 0.12);
  border-color: rgba(64, 158, 255, 0.3);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
}

.page-count {
  font-size: 12px;
  font-weight: 500;
  color: #909399;
  line-height: 1;
}

.divider {
  width: 1px;
  height: 12px;
  background: rgba(64, 158, 255, 0.3);
  margin: 0 2px;
}

.pagination-info {
  font-size: 12px;
  font-weight: 500;
  color: #409eff;
  letter-spacing: 0.5px;
  min-width: 24px;
  text-align: center;
  line-height: 1;
}

.pagination-toggle-icon {
  font-size: 12px;
  color: #409eff;
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.pagination-section {
  padding: 16px;
  border-top: 1px solid #f0f0f0;
  background: #fafafa;
}

.pagination-section .el-pagination {
  display: flex;
  justify-content: center;
}

.pagination-slide-enter-active,
.pagination-slide-leave-active {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.pagination-slide-enter-from,
.pagination-slide-leave-to {
  opacity: 0;
  max-height: 0;
  overflow: hidden;
}

.pagination-slide-enter-to,
.pagination-slide-leave-from {
  opacity: 1;
  max-height: 200px;
}

.action-buttons {
  display: flex;
  gap: 12px;
  flex-wrap: nowrap;
  white-space: nowrap;
  justify-content: flex-start;
  align-items: center;
}

/* 确保所有包装元素不影响间距 - 只重置上下和右的 margin，保留左 margin 用于控制间距 */
.action-buttons > * {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  margin-right: 0 !important;
  margin-left: 0;
  padding: 0;
  display: inline-flex;
}

/* 确保 el-tooltip 包装元素不影响间距 */
.action-buttons > :deep(.el-tooltip) {
  display: inline-flex;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  margin-right: 0 !important;
  margin-left: 0;
  padding: 0;
}

.action-buttons :deep(.el-tooltip__trigger) {
  display: inline-flex;
  margin: 0 !important;
  padding: 0;
}

/* 确保 el-dropdown 不影响间距 */
.action-buttons :deep(.el-dropdown) {
  display: inline-flex;
  margin: 0 !important;
  padding: 0;
}

/* 为第4个按钮（更多操作）添加额外左边距，使其与前3个按钮有更明显的视觉分隔 */
/* 这样前3个按钮之间是 12px（gap），第3个和第4个之间是 12px（gap）+ 8px（margin）= 20px */
.action-buttons > :nth-child(4) {
  margin-left: 8px !important;
}

.action-buttons :deep(.el-button) {
  font-weight: 500;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.action-buttons :deep(.el-button:hover) {
  transform: translateY(-2px) scale(1.05);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.action-buttons :deep(.el-button:active) {
  transform: translateY(0) scale(0.98);
}

.action-buttons :deep(.el-button.is-disabled) {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
}

.action-buttons :deep(.el-button.is-disabled:hover) {
  transform: none;
  box-shadow: none;
}

/* 下拉菜单内部按钮样式 */
.action-buttons :deep(.el-dropdown .el-button) {
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 0;
}

.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}

.detail-section {
  margin-top: 24px;
}

.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.section-title {
  font-weight: 600;
  color: #303133;
}

.section-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.json-block {
  max-height: 260px;
  overflow: auto;
  background: #f5f5f5;
  padding: 10px;
  border-radius: 4px;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 13px;
}

.json-collapsed-placeholder {
  padding: 12px;
  background: #f8f9fb;
  border: 1px dashed #dcdfe6;
  border-radius: 4px;
  color: #909399;
  font-size: 13px;
}

.json-action-divider {
  height: 20px;
  margin: 0 4px;
}

.detail-files-table,
.download-files-table {
  margin-top: 12px;
}

.empty-tip {
  padding: 24px;
  text-align: center;
  color: #909399;
  background: #f5f7fa;
  border-radius: 8px;
}

.field-tip {
  font-size: 12px;
  color: #909399;
  margin-top: 4px;
}

.webhook-option {
  display: flex;
  flex-direction: column;
}

.webhook-name {
  font-weight: 500;
}

.webhook-desc {
  font-size: 12px;
  color: #909399;
}

.json-toggle-icon {
  transition: transform 0.2s ease;
}

.json-toggle-icon.expanded {
  transform: rotate(180deg);
}

.json-collapse-enter-active,
.json-collapse-leave-active {
  transition: max-height 0.3s ease, opacity 0.3s ease;
}

.json-collapse-enter-from,
.json-collapse-leave-to {
  max-height: 0;
  opacity: 0;
}

.json-collapse-enter-to,
.json-collapse-leave-from {
  max-height: 400px;
  opacity: 1;
}
</style>
